#include "nuklear.h"
#include "nuklear_internal.h"

/* ===============================================================
 *
 *                              UTIL
 *
 * ===============================================================*/
NK_INTERN int nk_str_match_here(const char* regexp, const char* text);
NK_INTERN int nk_str_match_star(int c, const char* regexp, const char* text);
NK_LIB nk_bool nk_is_lower(int c) {
  return (c >= 'a' && c <= 'z') || (c >= 0xE0 && c <= 0xFF);
}
NK_LIB nk_bool nk_is_upper(int c) {
  return (c >= 'A' && c <= 'Z') || (c >= 0xC0 && c <= 0xDF);
}
NK_LIB int nk_to_upper(int c) {
  return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;
}
NK_LIB int nk_to_lower(int c) {
  return (c >= 'A' && c <= 'Z') ? (c - ('a' + 'A')) : c;
}

#ifdef NK_MEMCPY_IMPLEMENTATION
NK_LIB void*
nk_memcopy(void* dst0, const void* src0, nk_size length) {
  nk_ptr t;
  char* dst = (char*)dst0;
  const char* src = (const char*)src0;
  if (length == 0 || dst == src)
    goto done;

#define nk_word int
#define nk_wsize sizeof(nk_word)
#define nk_wmask (nk_wsize - 1)
#define NK_TLOOP(s) \
  if (t) \
  NK_TLOOP1(s)
#define NK_TLOOP1(s) \
  do { \
    s; \
  } while (--t)

  if (dst < src) {
    t = (nk_ptr)src; /* only need low bits */
    if ((t | (nk_ptr)dst) & nk_wmask) {
      if ((t ^ (nk_ptr)dst) & nk_wmask || length < nk_wsize)
        t = length;
      else
        t = nk_wsize - (t & nk_wmask);
      length -= t;
      NK_TLOOP1(*dst++ = *src++);
    }
    t = length / nk_wsize;
    NK_TLOOP(*(nk_word*)(void*)dst = *(const nk_word*)(const void*)src;
             src += nk_wsize;
             dst += nk_wsize);
    t = length & nk_wmask;
    NK_TLOOP(*dst++ = *src++);
  } else {
    src += length;
    dst += length;
    t = (nk_ptr)src;
    if ((t | (nk_ptr)dst) & nk_wmask) {
      if ((t ^ (nk_ptr)dst) & nk_wmask || length <= nk_wsize)
        t = length;
      else
        t &= nk_wmask;
      length -= t;
      NK_TLOOP1(*--dst = *--src);
    }
    t = length / nk_wsize;
    NK_TLOOP(src -= nk_wsize; dst -= nk_wsize;
             *(nk_word*)(void*)dst = *(const nk_word*)(const void*)src);
    t = length & nk_wmask;
    NK_TLOOP(*--dst = *--src);
  }
#undef nk_word
#undef nk_wsize
#undef nk_wmask
#undef NK_TLOOP
#undef NK_TLOOP1
done:
  return (dst0);
}
#endif
#ifdef NK_MEMSET_IMPLEMENTATION
NK_LIB void nk_memset(void* ptr, int c0, nk_size size) {
#define nk_word unsigned
#define nk_wsize sizeof(nk_word)
#define nk_wmask (nk_wsize - 1)
  nk_byte* dst = (nk_byte*)ptr;
  unsigned c = 0;
  nk_size t = 0;

  if ((c = (nk_byte)c0) != 0) {
    c = (c << 8) | c; /* at least 16-bits  */
    if (sizeof(unsigned int) > 2)
      c = (c << 16) | c; /* at least 32-bits*/
  }

  /* too small of a word count */
  dst = (nk_byte*)ptr;
  if (size < 3 * nk_wsize) {
    while (size--)
      *dst++ = (nk_byte)c0;
    return;
  }

  /* align destination */
  if ((t = NK_PTR_TO_UINT(dst) & nk_wmask) != 0) {
    t = nk_wsize - t;
    size -= t;
    do {
      *dst++ = (nk_byte)c0;
    } while (--t != 0);
  }

  /* fill word */
  t = size / nk_wsize;
  do {
    *(nk_word*)((void*)dst) = c;
    dst += nk_wsize;
  } while (--t != 0);

  /* fill trailing bytes */
  t = (size & nk_wmask);
  if (t != 0) {
    do {
      *dst++ = (nk_byte)c0;
    } while (--t != 0);
  }

#undef nk_word
#undef nk_wsize
#undef nk_wmask
}
#endif
NK_LIB void nk_zero(void* ptr, nk_size size) {
  NK_ASSERT(ptr);
  NK_MEMSET(ptr, 0, size);
}
NK_API int nk_strlen(const char* str) {
  int siz = 0;
  NK_ASSERT(str);
  while (str && *str++ != '\0')
    siz++;
  return siz;
}
NK_API int nk_strtoi(const char* str, const char** endptr) {
  int neg = 1;
  const char* p = str;
  int value = 0;

  NK_ASSERT(str);
  if (!str)
    return 0;

  /* skip whitespace */
  while (*p == ' ')
    p++;
  if (*p == '-') {
    neg = -1;
    p++;
  }
  while (*p && *p >= '0' && *p <= '9') {
    value = value * 10 + (int)(*p - '0');
    p++;
  }
  if (endptr)
    *endptr = p;
  return neg * value;
}
NK_API double nk_strtod(const char* str, const char** endptr) {
  double m;
  double neg = 1.0;
  const char* p = str;
  double value = 0;
  double number = 0;

  NK_ASSERT(str);
  if (!str)
    return 0;

  /* skip whitespace */
  while (*p == ' ')
    p++;
  if (*p == '-') {
    neg = -1.0;
    p++;
  }

  while (*p && *p != '.' && *p != 'e') {
    value = value * 10.0 + (double)(*p - '0');
    p++;
  }

  if (*p == '.') {
    p++;
    for (m = 0.1; *p && *p != 'e'; p++) {
      value = value + (double)(*p - '0') * m;
      m *= 0.1;
    }
  }
  if (*p == 'e') {
    int i, pow, div;
    p++;
    if (*p == '-') {
      div = nk_true;
      p++;
    } else if (*p == '+') {
      div = nk_false;
      p++;
    } else
      div = nk_false;

    for (pow = 0; *p; p++)
      pow = pow * 10 + (int)(*p - '0');

    for (m = 1.0, i = 0; i < pow; i++)
      m *= 10.0;

    if (div)
      value /= m;
    else
      value *= m;
  }
  number = value * neg;
  if (endptr)
    *endptr = p;
  return number;
}
NK_API float nk_strtof(const char* str, const char** endptr) {
  float float_value;
  double double_value;
  double_value = NK_STRTOD(str, endptr);
  float_value = (float)double_value;
  return float_value;
}
NK_API int nk_stricmp(const char* s1, const char* s2) {
  nk_int c1, c2, d;
  do {
    c1 = *s1++;
    c2 = *s2++;
    d = c1 - c2;
    while (d) {
      if (c1 <= 'Z' && c1 >= 'A') {
        d += ('a' - 'A');
        if (!d)
          break;
      }
      if (c2 <= 'Z' && c2 >= 'A') {
        d -= ('a' - 'A');
        if (!d)
          break;
      }
      return ((d >= 0) << 1) - 1;
    }
  } while (c1);
  return 0;
}
NK_API int nk_stricmpn(const char* s1, const char* s2, int n) {
  int c1, c2, d;
  NK_ASSERT(n >= 0);
  do {
    c1 = *s1++;
    c2 = *s2++;
    if (!n--)
      return 0;

    d = c1 - c2;
    while (d) {
      if (c1 <= 'Z' && c1 >= 'A') {
        d += ('a' - 'A');
        if (!d)
          break;
      }
      if (c2 <= 'Z' && c2 >= 'A') {
        d -= ('a' - 'A');
        if (!d)
          break;
      }
      return ((d >= 0) << 1) - 1;
    }
  } while (c1);
  return 0;
}
NK_INTERN int
nk_str_match_here(const char* regexp, const char* text) {
  if (regexp[0] == '\0')
    return 1;
  if (regexp[1] == '*')
    return nk_str_match_star(regexp[0], regexp + 2, text);
  if (regexp[0] == '$' && regexp[1] == '\0')
    return *text == '\0';
  if (*text != '\0' && (regexp[0] == '.' || regexp[0] == *text))
    return nk_str_match_here(regexp + 1, text + 1);
  return 0;
}
NK_INTERN int
nk_str_match_star(int c, const char* regexp, const char* text) {
  do { /* a '* matches zero or more instances */
    if (nk_str_match_here(regexp, text))
      return 1;
  } while (*text != '\0' && (*text++ == c || c == '.'));
  return 0;
}
NK_API int nk_strfilter(const char* text, const char* regexp) {
  /*
    c    matches any literal character c
    .    matches any single character
    ^    matches the beginning of the input string
    $    matches the end of the input string
    *    matches zero or more occurrences of the previous character*/
  if (regexp[0] == '^')
    return nk_str_match_here(regexp + 1, text);
  do { /* must look even if string is empty */
    if (nk_str_match_here(regexp, text))
      return 1;
  } while (*text++ != '\0');
  return 0;
}
NK_API int nk_strmatch_fuzzy_text(const char* str, int str_len,
                                  const char* pattern, int* out_score) {
/* Returns true if each character in pattern is found sequentially within str
     * if found then out_score is also set. Score value has no intrinsic meaning.
     * Range varies with pattern. Can only compare scores with same search pattern. */

/* bonus for adjacent matches */
#define NK_ADJACENCY_BONUS 5
/* bonus if match occurs after a separator */
#define NK_SEPARATOR_BONUS 10
/* bonus if match is uppercase and prev is lower */
#define NK_CAMEL_BONUS 10
/* penalty applied for every letter in str before the first match */
#define NK_LEADING_LETTER_PENALTY (-3)
/* maximum penalty for leading letters */
#define NK_MAX_LEADING_LETTER_PENALTY (-9)
/* penalty for every letter that doesn't matter */
#define NK_UNMATCHED_LETTER_PENALTY (-1)

  /* loop variables */
  int score = 0;
  char const* pattern_iter = pattern;
  int str_iter = 0;
  int prev_matched = nk_false;
  int prev_lower = nk_false;
  /* true so if first letter match gets separator bonus*/
  int prev_separator = nk_true;

  /* use "best" matched letter if multiple string letters match the pattern */
  char const* best_letter = 0;
  int best_letter_score = 0;

  /* loop over strings */
  NK_ASSERT(str);
  NK_ASSERT(pattern);
  if (!str || !str_len || !pattern)
    return 0;
  while (str_iter < str_len) {
    const char pattern_letter = *pattern_iter;
    const char str_letter = str[str_iter];

    int next_match = *pattern_iter != '\0' &&
                     nk_to_lower(pattern_letter) == nk_to_lower(str_letter);
    int rematch = best_letter && nk_to_upper(*best_letter) == nk_to_upper(str_letter);

    int advanced = next_match && best_letter;
    int pattern_repeat = best_letter && *pattern_iter != '\0';
    pattern_repeat = pattern_repeat &&
                     nk_to_lower(*best_letter) == nk_to_lower(pattern_letter);

    if (advanced || pattern_repeat) {
      score += best_letter_score;
      best_letter = 0;
      best_letter_score = 0;
    }

    if (next_match || rematch) {
      int new_score = 0;
      /* Apply penalty for each letter before the first pattern match */
      if (pattern_iter == pattern) {
        int count = (int)(&str[str_iter] - str);
        int penalty = NK_LEADING_LETTER_PENALTY * count;
        if (penalty < NK_MAX_LEADING_LETTER_PENALTY)
          penalty = NK_MAX_LEADING_LETTER_PENALTY;

        score += penalty;
      }

      /* apply bonus for consecutive bonuses */
      if (prev_matched)
        new_score += NK_ADJACENCY_BONUS;

      /* apply bonus for matches after a separator */
      if (prev_separator)
        new_score += NK_SEPARATOR_BONUS;

      /* apply bonus across camel case boundaries */
      if (prev_lower && nk_is_upper(str_letter))
        new_score += NK_CAMEL_BONUS;

      /* update pattern iter IFF the next pattern letter was matched */
      if (next_match)
        ++pattern_iter;

      /* update best letter in str which may be for a "next" letter or a rematch */
      if (new_score >= best_letter_score) {
        /* apply penalty for now skipped letter */
        if (best_letter != 0)
          score += NK_UNMATCHED_LETTER_PENALTY;

        best_letter = &str[str_iter];
        best_letter_score = new_score;
      }
      prev_matched = nk_true;
    } else {
      score += NK_UNMATCHED_LETTER_PENALTY;
      prev_matched = nk_false;
    }

    /* separators should be more easily defined */
    prev_lower = nk_is_lower(str_letter) != 0;
    prev_separator = str_letter == '_' || str_letter == ' ';

    ++str_iter;
  }

  /* apply score for last match */
  if (best_letter)
    score += best_letter_score;

  /* did not match full pattern */
  if (*pattern_iter != '\0')
    return nk_false;

  if (out_score)
    *out_score = score;
  return nk_true;
}
NK_API int nk_strmatch_fuzzy_string(char const* str, char const* pattern, int* out_score) {
  return nk_strmatch_fuzzy_text(str, nk_strlen(str), pattern, out_score);
}
NK_LIB int
nk_string_float_limit(char* string, int prec) {
  int dot = 0;
  char* c = string;
  while (*c) {
    if (*c == '.') {
      dot = 1;
      c++;
      continue;
    }
    if (dot == (prec + 1)) {
      *c = 0;
      break;
    }
    if (dot > 0)
      dot++;
    c++;
  }
  return (int)(c - string);
}
NK_INTERN void nk_strrev_ascii(char* s) {
  int len = nk_strlen(s);
  int end = len / 2;
  int i = 0;
  char t;
  for (; i < end; ++i) {
    t = s[i];
    s[i] = s[len - 1 - i];
    s[len - 1 - i] = t;
  }
}
NK_LIB char*
nk_itoa(char* s, long n) {
  long i = 0;
  if (n == 0) {
    s[i++] = '0';
    s[i] = 0;
    return s;
  }
  if (n < 0) {
    s[i++] = '-';
    n = -n;
  }
  while (n > 0) {
    s[i++] = (char)('0' + (n % 10));
    n /= 10;
  }
  s[i] = 0;
  if (s[0] == '-')
    ++s;

  nk_strrev_ascii(s);
  return s;
}
#ifdef NK_DTOA_IMPLEMENTATION
NK_LIB char*
nk_dtoa(char* s, double n) {
  int useExp = 0;
  int digit = 0, m = 0, m1 = 0;
  char* c = s;
  int neg = 0;

  NK_ASSERT(s);
  if (!s)
    return 0;

  if (n == 0.0) {
    s[0] = '0';
    s[1] = '\0';
    return s;
  }

  neg = (n < 0);
  if (neg)
    n = -n;

  /* calculate magnitude */
  m = nk_log10(n);
  useExp = (m >= 14 || (neg && m >= 9) || m <= -9);
  if (neg)
    *(c++) = '-';

  /* set up for scientific notation */
  if (useExp) {
    if (m < 0)
      m -= 1;
    n = n / (double)nk_pow(10.0, m);
    m1 = m;
    m = 0;
  }
  if (m < 1.0) {
    m = 0;
  }

  /* convert the number */
  while (n > NK_FLOAT_PRECISION || m >= 0) {
    double weight = nk_pow(10.0, m);
    if (weight > 0) {
      double t = (double)n / weight;
      digit = nk_ifloord(t);
      n -= ((double)digit * weight);
      *(c++) = (char)('0' + (char)digit);
    }
    if (m == 0 && n > 0)
      *(c++) = '.';
    m--;
  }

  if (useExp) {
    /* convert the exponent */
    int i, j;
    *(c++) = 'e';
    if (m1 > 0) {
      *(c++) = '+';
    } else {
      *(c++) = '-';
      m1 = -m1;
    }
    m = 0;
    while (m1 > 0) {
      *(c++) = (char)('0' + (char)(m1 % 10));
      m1 /= 10;
      m++;
    }
    c -= m;
    for (i = 0, j = m - 1; i < j; i++, j--) {
      /* swap without temporary */
      c[i] ^= c[j];
      c[j] ^= c[i];
      c[i] ^= c[j];
    }
    c += m;
  }
  *(c) = '\0';
  return s;
}
#endif
#ifdef NK_INCLUDE_STANDARD_VARARGS
#ifndef NK_INCLUDE_STANDARD_IO
NK_INTERN int
nk_vsnprintf(char* buf, int buf_size, const char* fmt, va_list args) {
  typedef enum {
    NK_ARG_TYPE_CHAR,
    NK_ARG_TYPE_SHORT,
    NK_ARG_TYPE_DEFAULT,
    NK_ARG_TYPE_LONG
  } nk_arg_type;
  typedef enum {
    NK_ARG_FLAG_LEFT = 0x01,
    NK_ARG_FLAG_PLUS = 0x02,
    NK_ARG_FLAG_SPACE = 0x04,
    NK_ARG_FLAG_NUM = 0x10,
    NK_ARG_FLAG_ZERO = 0x20
  } nk_arg_flags;

  char number_buffer[NK_MAX_NUMBER_BUFFER];
  nk_arg_type arg_type = NK_ARG_TYPE_DEFAULT;
  int precision = NK_DEFAULT;
  int width = NK_DEFAULT;
  nk_flags flag = 0;

  int len = 0;
  int result = -1;
  const char* iter = fmt;

  NK_ASSERT(buf);
  NK_ASSERT(buf_size);
  if (!buf || !buf_size || !fmt)
    return 0;
  for (iter = fmt; *iter && len < buf_size; iter++) {
    /* copy all non-format characters */
    while (*iter && (*iter != '%') && (len < buf_size))
      buf[len++] = *iter++;
    if (!(*iter) || len >= buf_size)
      break;
    iter++;

    /* flag arguments */
    while (*iter) {
      if (*iter == '-')
        flag |= NK_ARG_FLAG_LEFT;
      else if (*iter == '+')
        flag |= NK_ARG_FLAG_PLUS;
      else if (*iter == ' ')
        flag |= NK_ARG_FLAG_SPACE;
      else if (*iter == '#')
        flag |= NK_ARG_FLAG_NUM;
      else if (*iter == '0')
        flag |= NK_ARG_FLAG_ZERO;
      else
        break;
      iter++;
    }

    /* width argument */
    width = NK_DEFAULT;
    if (*iter >= '1' && *iter <= '9') {
      const char* end;
      width = nk_strtoi(iter, &end);
      if (end == iter)
        width = -1;
      else
        iter = end;
    } else if (*iter == '*') {
      width = va_arg(args, int);
      iter++;
    }

    /* precision argument */
    precision = NK_DEFAULT;
    if (*iter == '.') {
      iter++;
      if (*iter == '*') {
        precision = va_arg(args, int);
        iter++;
      } else {
        const char* end;
        precision = nk_strtoi(iter, &end);
        if (end == iter)
          precision = -1;
        else
          iter = end;
      }
    }

    /* length modifier */
    if (*iter == 'h') {
      if (*(iter + 1) == 'h') {
        arg_type = NK_ARG_TYPE_CHAR;
        iter++;
      } else
        arg_type = NK_ARG_TYPE_SHORT;
      iter++;
    } else if (*iter == 'l') {
      arg_type = NK_ARG_TYPE_LONG;
      iter++;
    } else
      arg_type = NK_ARG_TYPE_DEFAULT;

    /* specifier */
    if (*iter == '%') {
      NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT);
      NK_ASSERT(precision == NK_DEFAULT);
      NK_ASSERT(width == NK_DEFAULT);
      if (len < buf_size)
        buf[len++] = '%';
    } else if (*iter == 's') {
      /* string  */
      const char* str = va_arg(args, const char*);
      NK_ASSERT(str != buf && "buffer and argument are not allowed to overlap!");
      NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT);
      NK_ASSERT(precision == NK_DEFAULT);
      NK_ASSERT(width == NK_DEFAULT);
      if (str == buf)
        return -1;
      while (str && *str && len < buf_size)
        buf[len++] = *str++;
    } else if (*iter == 'n') {
      /* current length callback */
      signed int* n = va_arg(args, int*);
      NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT);
      NK_ASSERT(precision == NK_DEFAULT);
      NK_ASSERT(width == NK_DEFAULT);
      if (n)
        *n = len;
    } else if (*iter == 'c' || *iter == 'i' || *iter == 'd') {
      /* signed integer */
      long value = 0;
      const char* num_iter;
      int num_len, num_print, padding;
      int cur_precision = NK_MAX(precision, 1);
      int cur_width = NK_MAX(width, 0);

      /* retrieve correct value type */
      if (arg_type == NK_ARG_TYPE_CHAR)
        value = (signed char)va_arg(args, int);
      else if (arg_type == NK_ARG_TYPE_SHORT)
        value = (signed short)va_arg(args, int);
      else if (arg_type == NK_ARG_TYPE_LONG)
        value = va_arg(args, signed long);
      else if (*iter == 'c')
        value = (unsigned char)va_arg(args, int);
      else
        value = va_arg(args, signed int);

      /* convert number to string */
      nk_itoa(number_buffer, value);
      num_len = nk_strlen(number_buffer);
      padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0);
      if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE))
        padding = NK_MAX(padding - 1, 0);

      /* fill left padding up to a total of `width` characters */
      if (!(flag & NK_ARG_FLAG_LEFT)) {
        while (padding-- > 0 && (len < buf_size)) {
          if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT))
            buf[len++] = '0';
          else
            buf[len++] = ' ';
        }
      }

      /* copy string value representation into buffer */
      if ((flag & NK_ARG_FLAG_PLUS) && value >= 0 && len < buf_size)
        buf[len++] = '+';
      else if ((flag & NK_ARG_FLAG_SPACE) && value >= 0 && len < buf_size)
        buf[len++] = ' ';

      /* fill up to precision number of digits with '0' */
      num_print = NK_MAX(cur_precision, num_len);
      while (precision && (num_print > num_len) && (len < buf_size)) {
        buf[len++] = '0';
        num_print--;
      }

      /* copy string value representation into buffer */
      num_iter = number_buffer;
      while (precision && *num_iter && len < buf_size)
        buf[len++] = *num_iter++;

      /* fill right padding up to width characters */
      if (flag & NK_ARG_FLAG_LEFT) {
        while ((padding-- > 0) && (len < buf_size))
          buf[len++] = ' ';
      }
    } else if (*iter == 'o' || *iter == 'x' || *iter == 'X' || *iter == 'u') {
      /* unsigned integer */
      unsigned long value = 0;
      int num_len = 0, num_print, padding = 0;
      int cur_precision = NK_MAX(precision, 1);
      int cur_width = NK_MAX(width, 0);
      unsigned int base = (*iter == 'o') ? 8 : (*iter == 'u') ? 10 :
                                                                16;

      /* print oct/hex/dec value */
      const char* upper_output_format = "0123456789ABCDEF";
      const char* lower_output_format = "0123456789abcdef";
      const char* output_format = (*iter == 'x') ?
                                      lower_output_format :
                                      upper_output_format;

      /* retrieve correct value type */
      if (arg_type == NK_ARG_TYPE_CHAR)
        value = (unsigned char)va_arg(args, int);
      else if (arg_type == NK_ARG_TYPE_SHORT)
        value = (unsigned short)va_arg(args, int);
      else if (arg_type == NK_ARG_TYPE_LONG)
        value = va_arg(args, unsigned long);
      else
        value = va_arg(args, unsigned int);

      do {
        /* convert decimal number into hex/oct number */
        int digit = output_format[value % base];
        if (num_len < NK_MAX_NUMBER_BUFFER)
          number_buffer[num_len++] = (char)digit;
        value /= base;
      } while (value > 0);

      num_print = NK_MAX(cur_precision, num_len);
      padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0);
      if (flag & NK_ARG_FLAG_NUM)
        padding = NK_MAX(padding - 1, 0);

      /* fill left padding up to a total of `width` characters */
      if (!(flag & NK_ARG_FLAG_LEFT)) {
        while ((padding-- > 0) && (len < buf_size)) {
          if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT))
            buf[len++] = '0';
          else
            buf[len++] = ' ';
        }
      }

      /* fill up to precision number of digits */
      if (num_print && (flag & NK_ARG_FLAG_NUM)) {
        if ((*iter == 'o') && (len < buf_size)) {
          buf[len++] = '0';
        } else if ((*iter == 'x') && ((len + 1) < buf_size)) {
          buf[len++] = '0';
          buf[len++] = 'x';
        } else if ((*iter == 'X') && ((len + 1) < buf_size)) {
          buf[len++] = '0';
          buf[len++] = 'X';
        }
      }
      while (precision && (num_print > num_len) && (len < buf_size)) {
        buf[len++] = '0';
        num_print--;
      }

      /* reverse number direction */
      while (num_len > 0) {
        if (precision && (len < buf_size))
          buf[len++] = number_buffer[num_len - 1];
        num_len--;
      }

      /* fill right padding up to width characters */
      if (flag & NK_ARG_FLAG_LEFT) {
        while ((padding-- > 0) && (len < buf_size))
          buf[len++] = ' ';
      }
    } else if (*iter == 'f') {
      /* floating point */
      const char* num_iter;
      int cur_precision = (precision < 0) ? 6 : precision;
      int prefix, cur_width = NK_MAX(width, 0);
      double value = va_arg(args, double);
      int num_len = 0, frac_len = 0, dot = 0;
      int padding = 0;

      NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT);
      NK_DTOA(number_buffer, value);
      num_len = nk_strlen(number_buffer);

      /* calculate padding */
      num_iter = number_buffer;
      while (*num_iter && *num_iter != '.')
        num_iter++;

      prefix = (*num_iter == '.') ? (int)(num_iter - number_buffer) + 1 : 0;
      padding = NK_MAX(cur_width - (prefix + NK_MIN(cur_precision, num_len - prefix)), 0);
      if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE))
        padding = NK_MAX(padding - 1, 0);

      /* fill left padding up to a total of `width` characters */
      if (!(flag & NK_ARG_FLAG_LEFT)) {
        while (padding-- > 0 && (len < buf_size)) {
          if (flag & NK_ARG_FLAG_ZERO)
            buf[len++] = '0';
          else
            buf[len++] = ' ';
        }
      }

      /* copy string value representation into buffer */
      num_iter = number_buffer;
      if ((flag & NK_ARG_FLAG_PLUS) && (value >= 0) && (len < buf_size))
        buf[len++] = '+';
      else if ((flag & NK_ARG_FLAG_SPACE) && (value >= 0) && (len < buf_size))
        buf[len++] = ' ';
      while (*num_iter) {
        if (dot)
          frac_len++;
        if (len < buf_size)
          buf[len++] = *num_iter;
        if (*num_iter == '.')
          dot = 1;
        if (frac_len >= cur_precision)
          break;
        num_iter++;
      }

      /* fill number up to precision */
      while (frac_len < cur_precision) {
        if (!dot && len < buf_size) {
          buf[len++] = '.';
          dot = 1;
        }
        if (len < buf_size)
          buf[len++] = '0';
        frac_len++;
      }

      /* fill right padding up to width characters */
      if (flag & NK_ARG_FLAG_LEFT) {
        while ((padding-- > 0) && (len < buf_size))
          buf[len++] = ' ';
      }
    } else {
      /* Specifier not supported: g,G,e,E,p,z */
      NK_ASSERT(0 && "specifier is not supported!");
      return result;
    }
  }
  buf[(len >= buf_size) ? (buf_size - 1) : len] = 0;
  result = (len >= buf_size) ? -1 : len;
  return result;
}
#endif
NK_LIB int
nk_strfmt(char* buf, int buf_size, const char* fmt, va_list args) {
  int result = -1;
  NK_ASSERT(buf);
  NK_ASSERT(buf_size);
  if (!buf || !buf_size || !fmt)
    return 0;
#ifdef NK_INCLUDE_STANDARD_IO
  result = NK_VSNPRINTF(buf, (nk_size)buf_size, fmt, args);
  result = (result >= buf_size) ? -1 : result;
  buf[buf_size - 1] = 0;
#else
  result = nk_vsnprintf(buf, buf_size, fmt, args);
#endif
  return result;
}
#endif
NK_API nk_hash
nk_murmur_hash(const void* key, int len, nk_hash seed) {
/* 32-Bit MurmurHash3: https://code.google.com/p/smhasher/wiki/MurmurHash3*/
#define NK_ROTL(x, r) ((x) << (r) | ((x) >> (32 - r)))

  nk_uint h1 = seed;
  nk_uint k1;
  const nk_byte* data = (const nk_byte*)key;
  const nk_byte* keyptr = data;
  nk_byte* k1ptr;
  const int bsize = sizeof(k1);
  const int nblocks = len / 4;

  const nk_uint c1 = 0xcc9e2d51;
  const nk_uint c2 = 0x1b873593;
  const nk_byte* tail;
  int i;

  /* body */
  if (!key)
    return 0;
  for (i = 0; i < nblocks; ++i, keyptr += bsize) {
    k1ptr = (nk_byte*)&k1;
    k1ptr[0] = keyptr[0];
    k1ptr[1] = keyptr[1];
    k1ptr[2] = keyptr[2];
    k1ptr[3] = keyptr[3];

    k1 *= c1;
    k1 = NK_ROTL(k1, 15);
    k1 *= c2;

    h1 ^= k1;
    h1 = NK_ROTL(h1, 13);
    h1 = h1 * 5 + 0xe6546b64;
  }

  /* tail */
  tail = (const nk_byte*)(data + nblocks * 4);
  k1 = 0;
  switch (len & 3) {
    case 3:
      k1 ^= (nk_uint)(tail[2] << 16); /* fallthrough */
    case 2:
      k1 ^= (nk_uint)(tail[1] << 8u); /* fallthrough */
    case 1:
      k1 ^= tail[0];
      k1 *= c1;
      k1 = NK_ROTL(k1, 15);
      k1 *= c2;
      h1 ^= k1;
      break;
    default:
      break;
  }

  /* finalization */
  h1 ^= (nk_uint)len;
  /* fmix32 */
  h1 ^= h1 >> 16;
  h1 *= 0x85ebca6b;
  h1 ^= h1 >> 13;
  h1 *= 0xc2b2ae35;
  h1 ^= h1 >> 16;

#undef NK_ROTL
  return h1;
}
#ifdef NK_INCLUDE_STANDARD_IO
NK_LIB char*
nk_file_load(const char* path, nk_size* siz, nk_allocator* alloc) {
  char* buf;
  FILE* fd;
  long ret;

  NK_ASSERT(path);
  NK_ASSERT(siz);
  NK_ASSERT(alloc);
  if (!path || !siz || !alloc)
    return 0;

  fd = fopen(path, "rb");
  if (!fd)
    return 0;
  fseek(fd, 0, SEEK_END);
  ret = ftell(fd);
  if (ret < 0) {
    fclose(fd);
    return 0;
  }
  *siz = (nk_size)ret;
  fseek(fd, 0, SEEK_SET);
  buf = (char*)alloc->alloc(alloc->userdata, 0, *siz);
  NK_ASSERT(buf);
  if (!buf) {
    fclose(fd);
    return 0;
  }
  *siz = (nk_size)fread(buf, 1, *siz, fd);
  fclose(fd);
  return buf;
}
#endif
NK_LIB int
nk_text_clamp(const nk_user_font* font, const char* text,
              int text_len, float space, int* glyphs, float* text_width,
              nk_rune* sep_list, int sep_count) {
  int i = 0;
  int glyph_len = 0;
  float last_width = 0;
  nk_rune unicode = 0;
  float width = 0;
  int len = 0;
  int g = 0;
  float s;

  int sep_len = 0;
  int sep_g = 0;
  float sep_width = 0;
  sep_count = NK_MAX(sep_count, 0);

  glyph_len = nk_utf_decode(text, &unicode, text_len);
  while (glyph_len && (width < space) && (len < text_len)) {
    len += glyph_len;
    s = font->width(font->userdata, font->height, text, len);
    for (i = 0; i < sep_count; ++i) {
      if (unicode != sep_list[i])
        continue;
      sep_width = last_width = width;
      sep_g = g + 1;
      sep_len = len;
      break;
    }
    if (i == sep_count) {
      last_width = sep_width = width;
      sep_g = g + 1;
    }
    width = s;
    glyph_len = nk_utf_decode(&text[len], &unicode, text_len - len);
    g++;
  }
  if (len >= text_len) {
    *glyphs = g;
    *text_width = last_width;
    return len;
  } else {
    *glyphs = sep_g;
    *text_width = sep_width;
    return (!sep_len) ? len : sep_len;
  }
}
NK_LIB nk_vec2
nk_text_calculate_text_bounds(const nk_user_font* font,
                              const char* begin, int byte_len, float row_height, const char** remaining,
                              nk_vec2* out_offset, int* glyphs, int op) {
  float line_height = row_height;
  nk_vec2 text_size = nk_make_vec2(0, 0);
  float line_width = 0.0f;

  float glyph_width;
  int glyph_len = 0;
  nk_rune unicode = 0;
  int text_len = 0;
  if (!begin || byte_len <= 0 || !font)
    return nk_make_vec2(0, row_height);

  glyph_len = nk_utf_decode(begin, &unicode, byte_len);
  if (!glyph_len)
    return text_size;
  glyph_width = font->width(font->userdata, font->height, begin, glyph_len);

  *glyphs = 0;
  while ((text_len < byte_len) && glyph_len) {
    if (unicode == '\n') {
      text_size.x = NK_MAX(text_size.x, line_width);
      text_size.y += line_height;
      line_width = 0;
      *glyphs += 1;
      if (op == NK_STOP_ON_NEW_LINE)
        break;

      text_len++;
      glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len - text_len);
      continue;
    }

    if (unicode == '\r') {
      text_len++;
      *glyphs += 1;
      glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len - text_len);
      continue;
    }

    *glyphs = *glyphs + 1;
    text_len += glyph_len;
    line_width += (float)glyph_width;
    glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len - text_len);
    glyph_width = font->width(font->userdata, font->height, begin + text_len, glyph_len);
    continue;
  }

  if (text_size.x < line_width)
    text_size.x = line_width;
  if (out_offset)
    *out_offset = nk_make_vec2(line_width, text_size.y + line_height);
  if (line_width > 0 || text_size.y == 0.0f)
    text_size.y += line_height;
  if (remaining)
    *remaining = begin + text_len;
  return text_size;
}
